﻿namespace Mercury.ParticleEngine
{
    using System;
    using System.Diagnostics.Contracts;
    using System.Runtime.InteropServices;
    using System.Runtime.Serialization;

    /// <summary>
    /// Defines a data structure representing a Euclidean vector facing a particular direction,
    /// including a magnitude value.
    /// </summary>
    [Serializable]
    [StructLayout(LayoutKind.Sequential)]
    public struct Vector : ISerializable
    {
        public Vector(Axis axis, Single magnitude)
        {
            _x = axis._x * magnitude;
            _y = axis._y * magnitude;
            _z = axis._z * magnitude;

            Magnitude = magnitude;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Vector"/> struct.
        /// </summary>
        /// <param name="info">A serialization info instance.</param>
        /// <param name="context">A reference to the streaming context.</param>
        /// <exception cref="T:System.ArgumentNullException">
        /// Thrown if the value passed to the <paramref name="info"/> parameter is <c>null</c>.
        /// </exception>
        internal Vector(SerializationInfo info, StreamingContext context)
        {
            Contract.Requires<ArgumentNullException>(info != null);

            _x = info.GetSingle("x");
            _y = info.GetSingle("y");
            _z = info.GetSingle("z");

            Magnitude = Calculator.SquareRoot((_x * _x) + (_y * _y) + (_z * _z));
        }

        internal readonly Single _x;
        internal readonly Single _y;
        internal readonly Single _z;

        /// <summary>
        /// Gets the length or magnitude of the Euclidean vector.
        /// </summary>
        public readonly Single Magnitude;

        /// <summary>
        /// Gets the axis in which the vector is facing.
        /// </summary>
        /// <returns>A <see cref="Axis"/> value representing the direction the vector is facing.</returns>
        public Axis GetAxis()
        {
            return new Axis(_x, _y, _z);
        }

        /// <summary>
        /// Populates a <see cref="System.Runtime.Serialization.SerializationInfo"/> with the data
        /// needed to serialize the target object.
        /// </summary>
        /// <param name="info">The <see cref="System.Runtime.Serialization.SerializationInfo"/>
        /// to populate with data.</param>
        /// <param name="context">The destination (see System.Runtime.Serialization.StreamingContext)
        /// for this serialization.</param>
        /// <exception cref="T:System.ArgumentNullException">
        /// Thrown if the value passed to the <paramref name="info"/> parameter is <c>null</c>.
        /// </exception>
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("x", _x);
            info.AddValue("y", _y);
            info.AddValue("z", _z);
        }
    }
}